import mongoose, { Schema, Document, Model } from 'mongoose';
import bcrypt from 'bcrypt';
import { IUser } from '@/types';
import { config } from '@/config';

/**
 * 用户文档接口
 */
export interface IUserDocument extends Omit<IUser, 'id'>, Document {
  comparePassword(candidatePassword: string): Promise<boolean>;
  generatePasswordResetToken(): string;
  generateEmailVerificationToken(): string;
  isLocked(): boolean;
  incrementLoginAttempts(): Promise<void>;
  resetLoginAttempts(): void;
  getFullName(): string;
}

/**
 * 用户模型接口
 */
export interface IUserModel extends Model<IUserDocument> {
  findByEmail(email: string): Promise<IUserDocument | null>;
  findByUsername(username: string): Promise<IUserDocument | null>;
  findActiveUsers(): Promise<IUserDocument[]>;
  findByRole(role: string): Promise<IUserDocument[]>;
}

/**
 * 用户 Schema
 */
const userSchema = new Schema<IUserDocument>({
  username: {
    type: String,
    required: [true, '用户名不能为空'],
    unique: true,
    trim: true,
    minlength: [3, '用户名长度至少为3位'],
    maxlength: [20, '用户名长度不能超过20位'],
    match: [/^[a-zA-Z0-9_]+$/, '用户名只能包含字母、数字和下划线'],
  },
  email: {
    type: String,
    required: [true, '邮箱不能为空'],
    unique: true,
    lowercase: true,
    trim: true,
    match: [/^\S+@\S+\.\S+$/, '邮箱格式不正确'],
  },
  password: {
    type: String,
    required: [true, '密码不能为空'],
    minlength: [8, '密码长度至少为8位'],
    select: false, // 查询时默认不返回密码
  },
  firstName: {
    type: String,
    required: [true, '名字不能为空'],
    trim: true,
    maxlength: [50, '名字长度不能超过50位'],
  },
  lastName: {
    type: String,
    required: [true, '姓氏不能为空'],
    trim: true,
    maxlength: [50, '姓氏长度不能超过50位'],
  },
  phone: {
    type: String,
    trim: true,
    match: [/^1[3-9]\d{9}$/, '手机号格式不正确'],
  },
  avatar: {
    type: String,
    default: null,
  },
  bio: {
    type: String,
    maxlength: [500, '个人简介长度不能超过500位'],
    default: '',
  },
  dateOfBirth: {
    type: Date,
    default: null,
  },
  gender: {
    type: String,
    enum: ['male', 'female', 'other', 'prefer_not_to_say'],
    default: null,
  },
  location: {
    type: String,
    maxlength: [100, '位置信息长度不能超过100位'],
    default: '',
  },
  website: {
    type: String,
    match: [/^https?:\/\/.+/, 'URL格式不正确'],
    default: null,
  },
  role: {
    type: String,
    enum: ['admin', 'user', 'moderator', 'guest'],
    default: 'user',
  },
  status: {
    type: String,
    enum: ['active', 'inactive', 'suspended', 'pending', 'deleted'],
    default: 'pending',
  },
  emailVerified: {
    type: Boolean,
    default: false,
  },
  emailVerificationToken: {
    type: String,
    default: null,
    select: false,
  },
  emailVerificationExpires: {
    type: Date,
    default: null,
    select: false,
  },
  passwordResetToken: {
    type: String,
    default: null,
    select: false,
  },
  passwordResetExpires: {
    type: Date,
    default: null,
    select: false,
  },
  lastLoginAt: {
    type: Date,
    default: null,
  },
  loginAttempts: {
    type: Number,
    default: 0,
    select: false,
  },
  lockUntil: {
    type: Date,
    default: null,
    select: false,
  },
}, {
  timestamps: true,
  toJSON: {
    transform: function(_doc, ret) {
      delete ret.password;
      delete ret.emailVerificationToken;
      delete ret.emailVerificationExpires;
      delete ret.passwordResetToken;
      delete ret.passwordResetExpires;
      delete ret.loginAttempts;
      delete ret.lockUntil;
      delete ret.__v;
      return ret;
    },
  },
});

/**
 * 索引
 */
userSchema.index({ email: 1 });
userSchema.index({ username: 1 });
userSchema.index({ status: 1 });
userSchema.index({ role: 1 });
userSchema.index({ createdAt: -1 });

/**
 * 密码加密中间件
 */
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();

  try {
    this.password = await bcrypt.hash(this.password, config.bcrypt.saltRounds);
    next();
  } catch (error) {
    next(error as Error);
  }
});

/**
 * 实例方法
 */

// 比较密码
userSchema.methods.comparePassword = async function(candidatePassword: string): Promise<boolean> {
  return bcrypt.compare(candidatePassword, this.password);
};

// 生成密码重置令牌
userSchema.methods.generatePasswordResetToken = function(): string {
  const token = Math.random().toString(36).substring(2, 15) + 
                Math.random().toString(36).substring(2, 15);
  
  this.passwordResetToken = token;
  this.passwordResetExpires = new Date(Date.now() + 30 * 60 * 1000); // 30分钟后过期
  
  return token;
};

// 生成邮箱验证令牌
userSchema.methods.generateEmailVerificationToken = function(): string {
  const token = Math.random().toString(36).substring(2, 15) + 
                Math.random().toString(36).substring(2, 15);
  
  this.emailVerificationToken = token;
  this.emailVerificationExpires = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24小时后过期
  
  return token;
};

// 检查账户是否被锁定
userSchema.methods.isLocked = function(): boolean {
  return !!(this.lockUntil && this.lockUntil > new Date());
};

// 增加登录尝试次数
userSchema.methods.incrementLoginAttempts = async function(): Promise<void> {
  const maxAttempts = 5;
  const lockTime = 30 * 60 * 1000; // 30分钟

  // 如果已经锁定且锁定时间已过，重置
  if (this.lockUntil && this.lockUntil < new Date()) {
    this.loginAttempts = 1;
    this.lockUntil = undefined;
  } else {
    this.loginAttempts += 1;
    
    // 如果达到最大尝试次数，锁定账户
    if (this.loginAttempts >= maxAttempts && !this.isLocked()) {
      this.lockUntil = new Date(Date.now() + lockTime);
    }
  }
  
  await this.save();
};

// 重置登录尝试次数
userSchema.methods.resetLoginAttempts = function(): void {
  this.loginAttempts = 0;
  this.lockUntil = undefined;
};

// 获取全名
userSchema.methods.getFullName = function(): string {
  return `${this.firstName} ${this.lastName}`.trim();
};

/**
 * 静态方法
 */

// 根据邮箱查找用户
userSchema.statics.findByEmail = function(email: string) {
  return this.findOne({ email: email.toLowerCase() });
};

// 根据用户名查找用户
userSchema.statics.findByUsername = function(username: string) {
  return this.findOne({ username });
};

// 查找活跃用户
userSchema.statics.findActiveUsers = function() {
  return this.find({ status: 'active' });
};

// 根据角色查找用户
userSchema.statics.findByRole = function(role: string) {
  return this.find({ role });
};

/**
 * 虚拟字段
 */
userSchema.virtual('fullName').get(function() {
  return `${this.firstName} ${this.lastName}`.trim();
});

/**
 * 导出用户模型
 */
export const User = mongoose.model<IUserDocument, IUserModel>('User', userSchema);

export default User;